{
 "metadata": {
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.9-final"
  },
  "orig_nbformat": 2,
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3.7.9 64-bit",
   "metadata": {
    "interpreter": {
     "hash": "0600588c3b5f4418cbe7b5ebc6825b479f3bc010269d8b60d75058cdd010adfe"
    }
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2,
 "cells": [
  {
   "source": [
    "# 打印任务\n",
    "正常模式：每分钟5页，质量好  \n",
    "快速模式：每分钟10页"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "source": [
    "## 建模\n",
    "- 生成和提交打印任务  \n",
    "    确定生成概率：每小时有10个学生提交20个作业 -> 每180秒1个作业生成并提交 -> 以1 / 180的概率生成作业  \n",
    "    确定打印页数：1-20页概率相同\n",
    "\n",
    "- 实施打印  \n",
    "    开始打印时倒计时，计时为0开始下一份作业\n",
    "\n",
    "- 模拟时间  \n",
    "    要分别处理生成打印任务和实施打印\n",
    "\n",
    "## 模拟流程\n",
    "- 创建打印队列对象\n",
    "\n",
    "- 时间按照秒流逝\n",
    "    1. 生成打印任务，加入打印队列\n",
    "    2. 如果打印机空闲且队列不空，则去除队首作业打印，记录等待时间\n",
    "    3. 如果打印机忙，则按照打印速度打印\n",
    "    4. 如果当前作业打印完成，则打印机进入空闲\n",
    "\n",
    "- 时间用尽，开始统计平均等待时间\n",
    "\n",
    "- 等待时间  \n",
    "    生成作业时，记录生成的时间戳  \n",
    "    开始打印时，当前时间减去生成时间即可  \n",
    "\n",
    "- 作业的打印时间  \n",
    "    生成作业时，记录作业的页数  \n",
    "    开始打印时，页数除以打印速度即可"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from myqueue import Queue\n",
    "import random"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Printer:\n",
    "    \"\"\"模拟打印机的类\"\"\"\n",
    "\n",
    "    def __init__(self, ppm):\n",
    "        \"\"\"初始化\"\"\"\n",
    "        # 打印速度\n",
    "        self.pagerate = ppm\n",
    "        # 打印任务\n",
    "        self.currentTask = None\n",
    "        # 任务倒计时\n",
    "        self.timeRemaining = 0\n",
    "\n",
    "    def tick(self):\n",
    "        \"\"\"打印一秒\"\"\"\n",
    "        if self.currentTask != None:\n",
    "            self.timeRemaining = self.timeRemaining - 1\n",
    "            if self.timeRemaining <= 0:\n",
    "                self.currentTask = None\n",
    "\n",
    "    def busy(self):\n",
    "        \"\"\"判断是否打印忙\"\"\"\n",
    "        if self.currentTask != None:\n",
    "            return True\n",
    "        else:\n",
    "            return False\n",
    "\n",
    "    def startNext(self, newtask):\n",
    "        \"\"\"打印新作业\"\"\"\n",
    "        self.currentTask = newtask\n",
    "        self.timeRemaining = newtask.getPages() \\\n",
    "            * 60 / self.pagerate"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Task:\n",
    "    \"\"\"模拟打印对象\"\"\"\n",
    "\n",
    "    def __init__(self, time):\n",
    "        # 打印的开始时间\n",
    "        self.timestamp = time\n",
    "        # 打印页数\n",
    "        # randrange不包括右端，可以设置步长；randint包括右端，不可以设置步长\n",
    "        self.pages = random.randrange(1, 21)\n",
    "        \n",
    "    def getStamp(self):\n",
    "        \"\"\"获取此对象的时间戳\"\"\"\n",
    "        return self.timestamp\n",
    "\n",
    "    def getPages(self):\n",
    "        \"\"\"获取页数\"\"\"\n",
    "        return self.pages\n",
    "\n",
    "    def waitTime(self, currenttime):\n",
    "        \"\"\"等待时间\"\"\"\n",
    "        return currenttime - self.timestamp"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def newPrintTask():\n",
    "    \"\"\"生成打印任务，以 1 / 180 的概率产生任务\"\"\"\n",
    "    num = random.randrange(1, 181)\n",
    "    if num == 180:\n",
    "        return True\n",
    "    else:\n",
    "        return False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "def simulation(numSeconds, pagesPerMinute):\n",
    "    \"\"\"\n",
    "    模拟打印的过程\n",
    "    simulation(numSeconds, pagesPerMinute)\n",
    "    @param numSeconds 打印时间\n",
    "    @param pagesPerMinute 打印速率\n",
    "    \"\"\"\n",
    "\n",
    "    # 生成模拟打印机对象\n",
    "    labprinter = Printer(pagesPerMinute)\n",
    "    # 新建打印队列\n",
    "    printQueue = Queue()\n",
    "    # 等待时间列表\n",
    "    waitingtimes = []\n",
    "\n",
    "    # 开始循环\n",
    "    for currentSecond in range(numSeconds):\n",
    "        # 询问是否产生新任务\n",
    "        if newPrintTask():\n",
    "            # 产生了新任务\n",
    "            task = Task(currentSecond)\n",
    "            # 将任务添加到打印队列\n",
    "            printQueue.enqueue(task)\n",
    "\n",
    "        # 存在打印任务且打印机空闲\n",
    "        if (not labprinter.busy()) and (not printQueue.isEmpty()):\n",
    "            # 将任务从等待队列移除\n",
    "            nexttask = printQueue.dequeue()\n",
    "            # 计算该任务的等待时间\n",
    "            waitingtimes.append(nexttask.waitTime(currentSecond))\n",
    "            # 开始打印\n",
    "            labprinter.startNext(nexttask)\n",
    "\n",
    "        # 打印一秒\n",
    "        labprinter.tick()\n",
    "\n",
    "    # 计算平均打印时间\n",
    "    averageWait = sum(waitingtimes) / len(waitingtimes)\n",
    "    # 打印平均打印时间\n",
    "    print(\"Average Wait {:6.2f} secs {:3d} tasks remaining.\".format(averageWait, printQueue.size()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Average Wait   0.00 secs   0 tasks remaining.\n"
     ]
    }
   ],
   "source": [
    "simulation(200, 10)"
   ]
  }
 ]
}